package cn.east196.tfx.ui;


import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.stream.Collectors;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.jfinal.kit.PropKit;

import cn.east196.tfx.Context;
import cn.east196.tfx.db.DatabaseService;
import cn.east196.tfx.db.JdbcProp;
import cn.east196.tfx.db.Schema;
import cn.east196.tfx.db.TableField;
import cn.east196.xfx.StagePool;
import cn.east196.xfx.StageUtil;
import cn.east196.xfx.TreeLeaf;
import cn.east196.xfx.controller.ControllerDataInitializable;
import cn.east196.xfx.controls.MessageBox;
import cn.east196.xfx.controls.SimpleTreeItem;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.text.Text;
import javafx.stage.Modality;
import javafx.stage.Stage;

public class InitializerController implements Initializable {

  protected static Logger logger = LogManager.getLogger(InitializerController.class);

  protected static String CURRENT_STAGE_ID = "main";

  private JdbcProp jdbcProp;
  private boolean mListener = false;
  @FXML
  private ComboBox<Schema> dbTypeComboBox;
  @FXML
  private TreeView<SimpleTreeItem> dbTree;
  @FXML
  protected Button connButton;
  @FXML
  private TextField userNameField, passowrdField;
  @FXML
  private ComboBox<String> urlComboBox;
  private TreeItem<SimpleTreeItem> dbTreeRootItem;
  private List<TableField> tableFields;
  private String currentSchemaName, currentTableName;
  final ContextMenu tableContextMenu = new ContextMenu();
  private static String defaultAddress = "127.0.0.1:3306";//"jdbc:mysql://127.0.0.1:3306/";
  private static String defaultJDBCUserName = "root";
  private static String defaultJDBCPassword = "aFM4JrZIDgpGiqJn";
  private static String defaultJDBCDriver = "com.mysql.jdbc.Driver";
  Schema schema;
  List<TreeItem<SimpleTreeItem>> treeItems;

  @Override
  public void initialize(URL location, ResourceBundle resources) {
	  this.databaseService = Context.getBean(DatabaseService.class);
    initComponent();
    initMenus();
  }

  private void initTreeRoot(String rootName) {

    if (dbTree.getRoot() != null && dbTree.getRoot().getChildren().size() > 0) {
      dbTree.getRoot().getChildren().clear();
    }
    SimpleTreeItem simpleTreeItem = new SimpleTreeItem(rootName, "/images/tree_root.png");
    simpleTreeItem.setMessage(TreeLeaf.ROOT);
    dbTreeRootItem = new TreeItem<>(simpleTreeItem);
    dbTree.setRoot(dbTreeRootItem);
  }

  private void loadTables(TreeItem<SimpleTreeItem> clickItem) {
    dbTree.setCursor(Cursor.WAIT);
    String schemaName = clickItem.getValue().getLabelText();
    new Thread(() -> {
        treeItems = loadTables(schemaName);
      Platform.runLater(() -> {
        clickItem.getChildren().addAll(treeItems);
        clickItem.setExpanded(true);
        dbTree.setCursor(Cursor.DEFAULT);
      });
    }).start();
  }

  private void loadColumn(TreeItem<SimpleTreeItem> currentTableItem) {
    dbTree.setCursor(Cursor.WAIT);
    tableFields = new ArrayList<>();
      loadMySQLColumn(currentTableItem);
  }

  private void loadMySQLColumn(TreeItem<SimpleTreeItem> currentTableItem) {
    new Thread(() -> {
      tableFields = getTableFields(currentSchemaName, currentTableName);
      for (TableField field : tableFields) {
        String pngPath = "/images/tree_column.png";
        String nullable = "";
        String extra = "";
        if (field.getKey() == "Id") {
          pngPath = "/images/column_key.png";
        }
        if (field.getNullable() == "true") {
          nullable = " | " + "NotNull";
        }
        if (field.getExtra() == "auto_increment") {
          extra = " | " + "auto_increment";
        }
        SimpleTreeItem simpleTreeItem = new SimpleTreeItem(field.getFieldText() + " | " + field.getTypeText() + nullable + extra, pngPath);
        TreeItem<SimpleTreeItem> item = new TreeItem<>(simpleTreeItem);
        Platform.runLater(() -> currentTableItem.getChildren().add(item));
      }
      Platform.runLater(() -> {
        currentTableItem.setExpanded(true);
        dbTree.setCursor(Cursor.DEFAULT);
      });
    }).start();
  }

  private void initComponent() {
    ObservableList<Schema> dbTypeList = FXCollections.observableArrayList();
    dbTypeList.add(0, Schema.MYSQL);
    dbTypeList.add(1, Schema.ORACLE);
    dbTypeComboBox.setItems(dbTypeList);
    dbTypeComboBox.getSelectionModel().select(0);
    urlComboBox.setValue(defaultAddress);
    userNameField.setText(defaultJDBCUserName);
    passowrdField.setText(defaultJDBCPassword);

    connButton.setGraphic(new ImageView("/images/start.png"));
    dbTypeComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
      if (newValue == Schema.MYSQL) {
        urlComboBox.setValue(defaultAddress);
      }
      if (newValue == Schema.ORACLE) {
        urlComboBox.setValue("127.0.0.1:1521:XE");
      }
    });
    if (!mListener) {
      dbTree.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent e) -> {
        Node node = (Node) e.getTarget();
        if ((node instanceof Text || node instanceof ImageView || node instanceof SimpleTreeItem
            || node instanceof TreeCell) && dbTree.getRoot() != null
            && dbTree.getRoot().getChildren().size() > 0) {
          TreeItem<SimpleTreeItem> clickItem = dbTree.getSelectionModel().getSelectedItem();
          if (clickItem == null) {
            return;
          }
          TreeLeaf clickMessage = clickItem.getValue().getMessage();
          if (e.getButton() == MouseButton.PRIMARY && e.getClickCount() == 2) {
            if (clickItem.getChildren().size() == 0) {
              if (clickMessage == TreeLeaf.SCHEMA) {
                currentSchemaName = clickItem.getValue().getLabelText();
                loadTables(clickItem);
              } else if (clickMessage == TreeLeaf.TABLE) {
                currentTableName = clickItem.getValue().getLabelText();
                loadColumn(clickItem);
              }
            }
          } else if (e.getButton() == MouseButton.SECONDARY) {
            if (clickMessage == TreeLeaf.SCHEMA) {
              tableContextMenu.show(dbTree, e.getScreenX(), e.getScreenY());
            } else if (e.getButton() == MouseButton.PRIMARY) {
              tableContextMenu.hide();
            }
          }
        }
      });
    }
    mListener = true;
  }

  private void initMenus() {
    MenuItem generateModelItem = new MenuItem("Generate Java Model");
//    tableContextMenu.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
//      if (event.getTarget() instanceof ContextMenuContent)
//        tableContextMenu.hide();
//    });
    generateModelItem.setOnAction(event -> {
      List<String> tableNames = new ArrayList<>();
      TreeItem clickItem = dbTree.getSelectionModel().getSelectedItem();
      ObservableList<TreeItem<SimpleTreeItem>> children = clickItem.getChildren();
      if (children.size() == 0) {
        MessageBox.warn(CURRENT_STAGE_ID, "Please double click to initialize the database.");
        return;
      }
      children.forEach(treeItem -> tableNames.add(treeItem.getValue().getLabelText()));

      String resource = "/fxml/generateModel.fxml";
      StageUtil stageUtil = new StageUtil();
      Stage stage = stageUtil.initStage(resource, 790, 661, "Generate Java Model");//建个新Stage
      stage.initOwner(StagePool.get("main"));
      stage.initModality(Modality.WINDOW_MODAL);
      stage.setResizable(false);
      ControllerDataInitializable gc = stageUtil.getLoader().getController();
      Map dataMap = new HashMap();
      dataMap.put("currentSchemaName", currentSchemaName);
      dataMap.put("tableNames", tableNames);
      dataMap.put("schema", schema);
      dataMap.put("jdbcProp", jdbcProp);
      gc.initData(dataMap);//数据传到新Stage
      StagePool.put("generator", stage);
      stage.show();
    });
    tableContextMenu.getItems().add(generateModelItem);
  }

  public void initConn() {
    if (true) {
      connButton.setGraphic(new ImageView("/images/conn-loading.gif"));
      new Thread(() -> {
        initJdbc();
        Platform.runLater(() -> {
          initTreeRoot(jdbcProp.getUrl());
          if (schema == Schema.ORACLE) {
            SimpleTreeItem simpleTreeItem = new SimpleTreeItem(jdbcProp.getUserName().toUpperCase(), "/images/tree_schema.png");
            simpleTreeItem.setMessage(TreeLeaf.SCHEMA);
            dbTreeRootItem.getChildren().add(new TreeItem<>(simpleTreeItem));
          }
          if (schema == Schema.MYSQL) {
            dbTreeRootItem.getChildren().addAll(listSchema());
          }
          connButton.setGraphic(new ImageView("/images/stop.png"));
          urlComboBox.setDisable(true);
          userNameField.setDisable(true);
          passowrdField.setDisable(true);
          dbTypeComboBox.setDisable(true);
        });
      }).start();
    } else {
      connButton.setGraphic(new ImageView("/images/start.png"));
      urlComboBox.setDisable(false);
      userNameField.setDisable(false);
      passowrdField.setDisable(false);
      dbTypeComboBox.setDisable(false);
      dbTree.setRoot(null);
      jdbcProp = null;
    }
  }

  public void about() {
	  PropKit.use("config/config.properties");
    MessageBox.showResult(StagePool.get("main"), Alert.AlertType.INFORMATION, "About",
        String.format("%s (%s)", PropKit.get("app.title"), PropKit.get("app.version")));
  }

  private void initJdbc() {
    jdbcProp = new JdbcProp();
    switch (dbTypeComboBox.getValue()) {
      case MYSQL:
        schema = Schema.MYSQL;
        jdbcProp.setDriverName(defaultJDBCDriver);
        jdbcProp.setUrl(String.format("jdbc:mysql://%s/", urlComboBox.getValue().trim()));
        break;
      case ORACLE:
        logger.warn("暂时不支持oracle");
    }

    jdbcProp.setUserName(userNameField.getText().trim());
    jdbcProp.setPassword(passowrdField.getText().trim());

  }
  
	public List<TreeItem<SimpleTreeItem>> loadTables(String schemaName) {
		List<TreeItem<SimpleTreeItem>> treeItems = new ArrayList<>();
		treeItems = databaseService.tables(schemaName).stream().map((tableName) -> {
			SimpleTreeItem simpleTreeItem = new SimpleTreeItem(tableName, "/images/tree_table.png");
			simpleTreeItem.setMessage(TreeLeaf.TABLE);
			return new TreeItem<>(simpleTreeItem);
		}).collect(Collectors.toList());
		return treeItems;
	}

	public List<TableField> getTableFields(String currentSchemaName, String currentTableName) {
		return databaseService.fs(currentSchemaName, currentTableName);
	}

	public List<TreeItem<SimpleTreeItem>> listSchema() {
		List<TreeItem<SimpleTreeItem>> treeItems = new ArrayList<>();
		treeItems = databaseService.schemas().stream().map((schemaName) -> {
			SimpleTreeItem simpleTreeItem = new SimpleTreeItem(schemaName, "/images/tree_schema.png");
			simpleTreeItem.setMessage(TreeLeaf.SCHEMA);
			return new TreeItem<>(simpleTreeItem);
		}).collect(Collectors.toList());
		return treeItems;
	}
	
	DatabaseService databaseService;
}
